Cambiar de la programación secuencial en CPU a la programación en GPU requiere un cambio de paradigma: del recorrido elemento a elemento al ejecución basada en bloques. Ya no vemos los datos como una secuencia de escalares, sino como colecciones de "bloques" programados para aprovechar al máximo el ancho de banda del hardware.
1. Limitado por memoria frente a limitado por cálculo
El cuello de botella de un kernel se determina por la relación entre operaciones matemáticas y accesos a memoria. La suma de vectores suele estar limitada por memoria porque realiza solo una suma por cada tres operaciones de memoria (2 cargas, 1 almacenamiento). El hardware pasa más tiempo esperando que llegue la DRAM que calculando.
2. El papel de BLOCK_SIZE
BLOCK_SIZE define la granularidad de la paralelización. Si es demasiado pequeño, subutilizamos las amplias vías de ejecución de la GPU. Un tamaño óptimo asegura suficiente "trabajo en vuelo" para saturar el bus de memoria.
3. Ocultar latencia mediante ocupación
Ocupación es el número de bloques activos en la GPU. Aunque no es el objetivo final, permite al planificador intercambiar un nuevo bloque para realizar cálculos mientras otro espera la recuperación de memoria de alta latencia desde la VRAM.
4. Utilización del hardware
Para maximizar el rendimiento, debemos alinear nuestro BLOCK_SIZE con las reglas de coalescencia de memoria de la arquitectura de la GPU, asegurando que los hilos consecutivos accedan a direcciones de memoria consecutivas.